#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include "Mesh.h"
#include "Shader.h"
#include "Texture.h"

Mesh *mesh;
Texture* texture;
Shader* shader;
GLint width, height;

glm::vec3 cubePositions[] = {
	glm::vec3(0.0f,  0.0f,  0.0f),
	glm::vec3(2.0f,  5.0f, -15.0f),
	glm::vec3(-1.5f, -2.2f, -2.5f),
	glm::vec3(-3.8f, -2.0f, -12.3f),
	glm::vec3(2.4f, -0.4f, -3.5f),
	glm::vec3(-1.7f,  3.0f, -7.5f),
	glm::vec3(1.3f, -2.0f, -2.5f),
	glm::vec3(1.5f,  2.0f, -2.5f),
	glm::vec3(1.5f,  0.2f, -1.5f),
	glm::vec3(-1.3f,  1.0f, -1.5f)
};

void init();
void display();

int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

	GLFWwindow* window = glfwCreateWindow(600, 600, "Transform", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "create window failed!" << std::endl;
		return 1;
	}
	glfwMakeContextCurrent(window);

	glewExperimental = GL_TRUE;
	if (GLEW_OK != glewInit())
	{
		std::cout << "init glew failed!" << std::endl;
		return 2;
	}

	glfwGetFramebufferSize(window, &width, &height);
	glViewport(0, 0, width, height);

	init();
	while (!glfwWindowShouldClose(window))
	{
		glfwPollEvents();
		display();
		glfwSwapBuffers(window);
	} 

	glfwTerminate();
	delete mesh;
	delete texture;
	delete shader;
	return 0;
}

void init()
{
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glEnable(GL_DEPTH_TEST);

	GLfloat vertices[] = {
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
		0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
	};
	//GLuint indices[] = {
	//	0, 1, 2,
	//	0, 3, 2,
	//	1, 5, 6,
	//	1, 2, 6,
	//	0, 3, 7,
	//	0, 4, 7,
	//	0, 1, 5,
	//	0, 4, 5,
	//	3, 2, 6,
	//	3, 7, 6,
	//	4, 5, 6,
	//	4, 7, 6
	//};
	GLuint attri_len[] = { 3,2 };

	mesh = new Mesh(vertices, 1, 1, 36, 2, 5, attri_len);
	shader = new Shader("res/vert.vs", "res/frag.fs");
	texture = new Texture("res/face.png", 1);
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	shader->use();

	GLint location = glGetUniformLocation(shader->getProgram(), "Texture");
	texture->bind(location, 0);

	glm::mat4 model, view, projection;
	//model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
	//model = glm::rotate(model, (GLfloat)glfwGetTime(), glm::vec3(0.5, 1.0f, 0.0f));
	view = glm::translate(view, glm::vec3(0.0f, 0.0f, -4.0f));
	projection = glm::perspective(glm::radians(45.0f), 1.0f * width / height, 0.1f, 100.0f);

	//location = glGetUniformLocation(shader->getProgram(), "model");
	//glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(model));
	location = glGetUniformLocation(shader->getProgram(), "view");
	glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(view));
	location = glGetUniformLocation(shader->getProgram(), "projection");
	glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(projection));

	mesh->bind();

	for (int i = 0; i < 10; i++)
	{
		glm::mat4 model;
		model = glm::translate(model, cubePositions[i]);
		model = glm::rotate(model, 2.0f * i + 0.2f, glm::vec3(1.0f, 0.3f, 0.5f));
		location = glGetUniformLocation(shader->getProgram(), "model");
		glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(model));
		glDrawArrays(GL_TRIANGLES, 0, 36);
	}
}